#include <stdio.h>

#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <SDL/SDL.h>
#include <SDL/SDL_video.h>
#include <libswscale/swscale.h>

static void log_callback_help(void *ptr, int level, const char *fmt, va_list vl)
{
    vfprintf(stdout, fmt, vl);
}

int main(int argc, const char *argv[]) {
    AVFormatContext *input_afc = NULL;
    const char *file_path = "../../mp4s/bird.mp4";

    av_register_all();

    // av_log_set_level(AV_LOG_TRACE);
    // av_log_set_callback(log_callback_help);

    int ret = 0;
    if ( (ret = avformat_open_input(&input_afc, file_path, NULL, NULL)) != 0 ) {
        printf("Couldn't open input stream: %s\n", av_err2str(ret));
        return -1;
    }

    if(avformat_find_stream_info(input_afc, NULL) < 0 ) {
		printf("Couldn't find stream information.\n");
		return -1;
    }

    int video_idx = -1;
    for(int i = 0; i < input_afc->nb_streams; i++) {
        if(input_afc->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_idx = i;
        }
    }
    if(video_idx < 0) {
        printf("Didn't find a video stream.\n");
		return -1;
    }
    printf("video index: %d\n", video_idx);

    AVCodecContext *input_cctx = input_afc->streams[video_idx]->codec;
    AVCodec *input_c = avcodec_find_decoder(input_cctx->codec_id);
    if(input_c == NULL) {
        printf("Codec not found.\n");
		return -1;
    }
    printf("finded codec: %s\n", input_c->name);

    if(avcodec_open2(input_cctx, input_c, NULL) < 0) {
        printf("Could not open codec.\n");
		return -1;
    }

    AVFrame *input_frame = av_frame_alloc();
    AVFrame *yuv_frame = av_frame_alloc();
    struct SwsContext *sws_ctx = NULL;

    // const SDL_VideoInfo *vi = SDL_GetVideoInfo();
    // int width = vi->current_w, height = vi->current_h;
    int width = input_cctx->width;
    int height = input_cctx->height;
    SDL_Surface *surface = SDL_SetVideoMode(width, height, 0, 0);
    if(surface == NULL) {
        printf("SDL: could not set video mode - exiting:%s\n",SDL_GetError());  
		return -1;
    }

    SDL_Overlay *overlay = SDL_CreateYUVOverlay(width, height, SDL_YV12_OVERLAY, surface);

    int got_picture = 0;

    AVPacket pkt;
    printf("------------- File Information ------------------\n");
    av_dump_format(input_afc, video_idx, file_path, 0);
	printf("-------------------------------------------------\n");

    SDL_WM_SetCaption("Simplest FFmpeg Player", NULL);
    sws_ctx = sws_getContext(input_cctx->width, input_cctx->height, input_cctx->pix_fmt, input_cctx->width, input_cctx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);

    SDL_Rect rect;
    rect.h = height;
    rect.w = width;
    rect.x = 0;
    rect.y = 0;

    while(av_read_frame(input_afc, &pkt) >= 0) {
        if(pkt.stream_index == video_idx) {
            ret = avcodec_decode_video2(input_cctx, input_frame, &got_picture, &pkt);
            if(ret < 0) {
                printf("Decode Error.\n");
				return -1;
            }
            if(got_picture){
                SDL_LockYUVOverlay(overlay);
                yuv_frame->data[0] = overlay->pixels[0];
                yuv_frame->data[1] = overlay->pixels[1];
                yuv_frame->data[2] = overlay->pixels[2];

                yuv_frame->linesize[0] = overlay->pitches[0];
                yuv_frame->linesize[1] = overlay->pitches[1];
                yuv_frame->linesize[2] = overlay->pitches[2];

                sws_scale(sws_ctx, (const uint8_t * const *)input_frame->data, input_frame->linesize,  0, input_cctx->height, yuv_frame->data, yuv_frame->linesize);

                SDL_UnlockYUVOverlay(overlay);
                SDL_DisplayYUVOverlay(overlay, &rect);
    
                SDL_Delay(40);
            }
        }
    }

    sws_freeContext(sws_ctx);
    SDL_Quit();
    avcodec_close(input_c);
    avformat_close_input(input_afc);
}
